package org.example.simple_1.stateMachineConfig;

import org.springframework.messaging.Message;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;
import org.springframework.stereotype.Component;

/**
 * 我将状态枚举和 事件枚举  状态机的builder 封装在一起
 *  以后每个状态机都有一个单独的这样的配置类
 *
 *  注意我们采用的的是builder的方式,位的就是创建多个状态机, 每个状态机也不能是单例的,所以就不要用spring 的注解了 如: @compent
 *
 *  枚举固然好,但是真实生产的时候,状态时放在库里的,一般都是string or  integer  等等的值,跟枚举没啥关系,所以采用常量值来表示的方式更适合
 *      然后常量的值根据具体的库里的值来是 string  or  integer  or 其他任意的值;
 *
 */
public class OrderStateMachine {

    // ===== 内部接口：定义所有状态常量 =====
    public interface States {
        String NEW = "NEW";
        String PROCESSING = "PROCESSING";
        String PAYMENT_PENDING = "PAYMENT_PENDING";
        String SHIPPED = "SHIPPED";
        String DELIVERED = "DELIVERED";
        String CANCELLED = "CANCELLED";
    }

    // ===== 内部接口：定义所有事件常量 =====
    public interface Events {
        String START_PROCESS = "START_PROCESS";
        String PAY = "PAY";
        String SHIP = "SHIP";
        String DELIVER = "DELIVER";
        String CANCEL = "CANCEL";
        String REJECT = "REJECT";
        String APPROVE = "APPROVE";
    }

    // ===== 静态方法：构建状态机 =====
    public static StateMachine<String, String> buildMachine(String initialState) throws Exception {
        StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();

        builder.configureConfiguration()
                .withConfiguration()
                // .autoStartup(true);    // 我们有手动的启动状态机,不需要设置这个了
                        .listener(listener());

        // 添加状态
        builder.configureStates()
                .withStates()
                .initial(initialState)
                .state(States.PROCESSING)
                .state(States.PAYMENT_PENDING)
                .state(States.SHIPPED)
                .end(States.DELIVERED)
                .end(States.CANCELLED);

        // 添加转换规则
        builder.configureTransitions()
                .withExternal()
                .source(States.NEW).target(States.PROCESSING).event(Events.START_PROCESS)
                .and()
                .withExternal()
                .source(States.NEW).target(States.CANCELLED).event(Events.CANCEL)
                .and()
                .withExternal()
                .source(States.PROCESSING).target(States.PAYMENT_PENDING).event(Events.PAY).action(action1())
                .and()
                .withExternal()
                .source(States.PROCESSING).target(States.CANCELLED).event(Events.CANCEL)
                .and()
                .withExternal()
                .source(States.PAYMENT_PENDING).target(States.SHIPPED).event(Events.SHIP)
                .and()
                .withExternal()
                .source(States.SHIPPED).target(States.DELIVERED).event(Events.DELIVER)
                .and()
                .withExternal()
                .source(States.PAYMENT_PENDING).target(States.CANCELLED).event(Events.REJECT)
                .and()
                .withExternal()
                .source(States.PAYMENT_PENDING).target(States.SHIPPED).event(Events.APPROVE);

        return builder.build();
    }

    public static Action<String, String> action1() {

        return (context) -> System.out.println("=============Action1 executed");
    }

    public static StateMachineListener<String, String> listener() {
        return new StateMachineListenerAdapter<String, String>() {
            @Override
            public void stateChanged(State<String, String> from, State<String, String> to) {
                System.out.println("=============State changed from " + from + " to " + to);
            }

            // 如果有未处理的事件,就走这个监听
            @Override
            public void eventNotAccepted(Message<String> event) {
                System.out.println("=============Event not accepted: " + event);
                throw new RuntimeException("!!!!!!!!!!!!!!!!!Event not accepted: " + event);
            }

        };
    }
}
